package com.ktwlrj.core.util;

import com.ktwlrj.core.constant.ResultCode;
import com.ktwlrj.core.exception.ActionException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.web.cors.CorsConfiguration;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 文件处理工具类
 *
 * @author zhouxuanhong
 * @date 2019-08-01 22:11
 * @since JDK1.8
 */
@Slf4j
public class FileUtils {

    private static final String CACHE_PATH = "./";

    /**
     * 多文件文件文件压缩进zip中并二进制流传输给前端
     *
     * @param byteArrayInputStreams ${@link ByteArrayInputStream}
     * @param fileNames             ${@link String}
     * @author zhouxuanhong
     * @date 2019-08-02 12:05
     */
    public static void multiFileToZipAndDownload(ByteArrayInputStream[] byteArrayInputStreams, String[] fileNames, HttpServletResponse response, String zipFileName) throws ActionException {
        String cacheFile = FileUtils.multiFileToZip(byteArrayInputStreams, fileNames);
        download(zipFileName, cacheFile, response);
    }


    /**
     * 文件二进制流传输给前端
     *
     * @param fileName  ${@link String}
     * @param outStream ${@link ByteArrayOutputStream}
     * @param response  ${@link HttpServletResponse}
     * @author zhouxuanhong
     * @date 2019-08-02 14:05
     */
    public static void download(ByteArrayOutputStream outStream, HttpServletResponse response, String fileName) throws ActionException {
        ByteArrayInputStream byteArrayInputStream = null;
        OutputStream outputStream = null;
        try {
            byteArrayInputStream = new ByteArrayInputStream(outStream.toByteArray());
            outputStream = response.getOutputStream();
            String encode = URLEncoder.encode(fileName, "UTF-8");
            response.setContentLengthLong(outStream.size());
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader("Access-Control-Allow-Origin", CorsConfiguration.ALL);
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Content-Disposition", "attachment; filename=" + encode);
            IOUtils.copy(byteArrayInputStream, outputStream);
        } catch (IOException e) {
            throw new ActionException(ResultCode.EXIST_OUT_RELATION.getValue(), "下载文件失败");
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
                if (byteArrayInputStream != null) {
                    byteArrayInputStream.close();
                }
            } catch (IOException io) {
                log.debug("----------FileUtils---------->download:{}", io.getMessage());
            }
        }
    }

    /**
     * 单文件文件压缩进zip中
     *
     * @param zipOutputStream      ${@link ZipOutputStream}
     * @param byteArrayInputStream ${@link ByteArrayInputStream}
     * @param fileName             ${@link String}
     * @author zhouxuanhong
     * @date 2019-08-02 12:05
     */
    private static void fileToZip(ZipOutputStream zipOutputStream, ByteArrayInputStream byteArrayInputStream, String fileName) throws IOException {
        try {
            //创建ZIP实体，并添加进压缩包
            ZipEntry zipEntry = new ZipEntry(fileName);
            zipOutputStream.putNextEntry(zipEntry);
            int read = 0;
            int readLen = 1024 * 10;
            byte[] bytes = new byte[readLen];
            while ((read = byteArrayInputStream.read(bytes, 0, readLen)) != -1) {
                zipOutputStream.write(bytes, 0, read);
            }
        } finally {
            if (byteArrayInputStream != null) {
                byteArrayInputStream.close();
            }
        }
    }


    /**
     * 多文件文件文件压缩进zip中
     *
     * @param byteArrayInputStreams ${@link ByteArrayInputStream[]}
     * @param fileNames             ${@link String[]}
     * @return String ${@link String} 生成缓存文件地址
     * @author zhouxuanhong
     * @date 2019-08-09 11:04
     */
    private static String multiFileToZip(ByteArrayInputStream[] byteArrayInputStreams, String[] fileNames) throws ActionException {
        int fileLen = byteArrayInputStreams.length;
        int fileNameLen = fileNames.length;
        // 检测待压缩文件个数与文件名称个数是否一致
        if (fileLen != fileNameLen) {
            throw new ActionException(ResultCode.EXIST_OUT_RELATION.getValue(), "待压缩文件个数与名称个数不对应");
        }
        String cacheFileName = CACHE_PATH + KtwlStringUtils.getUuId() + ".zip";
        FileOutputStream fileOutputStream;
        ZipOutputStream zipOutputStream = null;
        try {
            // 创建本地缓存zip文件
            File cacheFile = new File(cacheFileName);
            if (!cacheFile.exists()) {
                boolean newFile = cacheFile.createNewFile();
                if (!newFile) {
                    throw new ActionException(ResultCode.EXIST_OUT_RELATION.getValue(), "下载文件失败");
                }
            }
            // 循环把文件添加进压缩文件
            fileOutputStream = new FileOutputStream(cacheFile);
            zipOutputStream = new ZipOutputStream(fileOutputStream);
            for (int i = 0; i < fileLen; i++) {
                fileToZip(zipOutputStream, byteArrayInputStreams[i], fileNames[i]);
            }
            zipOutputStream.flush();
        } catch (IOException io) {
            log.debug("----------FileUtils---------->multiFileToZip:{}", io.getMessage());
            throw new ActionException(ResultCode.EXIST_OUT_RELATION.getValue(), "下载文件失败");
        } finally {
            try {
                // 关闭数据流要先关闭zip,否则下载文件大小有问题
                if (zipOutputStream != null) {
                    zipOutputStream.closeEntry();
                    zipOutputStream.close();
                }
            } catch (IOException e) {
                log.debug("----------FileUtils---------->multiFileToZip:{}", e.getMessage());
            }

        }
        return cacheFileName;
    }


    /**
     * 文件二进制流传输给前端
     *
     * @param fileName ${@link String} 传输给前端文件名字,包括扩展名
     * @param filePath ${@link String} 待下载文件地址
     * @param response ${@link HttpServletResponse} response
     * @author zhouxuanhong
     * @date 2019-08-09 11:05
     */
    public static void download(String fileName, String filePath, HttpServletResponse response) throws ActionException {
        File file = new File(filePath);
        FileInputStream fileInputStream = null;
        OutputStream outputStream = null;
        try {
            String encode = URLEncoder.encode(fileName, "UTF-8");
            response.setContentLengthLong(file.length());
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader("Access-Control-Allow-Origin", CorsConfiguration.ALL);
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Content-Disposition", "attachment; filename=" + encode);
            fileInputStream = new FileInputStream(file);
            outputStream = response.getOutputStream();
            IOUtils.copy(fileInputStream, outputStream);
        } catch (IOException io) {
            throw new ActionException(ResultCode.EXIST_OUT_RELATION.getValue(), "下载文件失败");
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
                if (file.exists()) {
                    boolean delete = file.delete();
                    log.debug("----------FileUtils----删除zip下载缓存成功------>download:{}", delete);
                }
            } catch (IOException io) {
                log.debug("----------FileUtils---------->download:{}", io.getMessage());
            }

        }
    }


    /**
     * 删除文件夹或文件
     *
     * @param directory ${@link File} 待删除的文件
     * @author zhouxuanhong
     * @date 2019-10-28 22:27
     */
    public static void delAllFile(File directory) {
        if (!directory.isDirectory()) {
            directory.delete();
        } else {
            File[] files = directory.listFiles();
            // 空文件夹
            if (files.length == 0) {
                directory.delete();
                return;
            }
            // 删除子文件夹和子文件
            for (File file : files) {
                if (file.isDirectory()) {
                    delAllFile(file);
                } else {
                    file.delete();
                }
            }
            // 删除文件夹本身
            directory.delete();
        }
    }


    /**
     * 删除文件夹或文件
     *
     * @param directory ${@link File} 待删除的文件名称或路径
     * @author zhouxuanhong
     * @date 2019-10-28 22:27
     */
    public static void delAllFile(String directory) {
        delAllFile(new File(directory));
    }

}
